package org.vectrics.calendar;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.apache.log4j.Logger;
import org.vectrics.common.db.Expirable;
import org.vectrics.common.db.ExpirableRecordObject;
import org.vectrics.common.db.ExpirableUtils;
import org.vectrics.common.db.ValidationProblemList;

/**
 * This class represents the best way to display the expirables passed to this method.  The information
 * includes the number of lanes needed (the max expirables displayed side by side) and for
 * each expirable, the recommended lane for display.
 * @param expirables
 * @return the display info
 */
public class CalendarItemsDisplayInfo extends ArrayList<CalendarItemDisplayInfo> {
	private static final long serialVersionUID = 893249723495324323L;
	private int numberOfRequiredLanes = 1;
	private Logger log = Logger.getLogger(this.getClass());

	public CalendarItemsDisplayInfo(Collection expirableRecordObjects) {
	    List sortedExpirables = ExpirableUtils.sortExpirablesByStartDate(expirableRecordObjects);
	    int maxLaneNumber = 0;
	    
	    Object[] array = sortedExpirables.toArray();
	    for (int i = 0; i < array.length; i++) {
	    	CalendarItemDisplayInfo itemDisplayInfo = new CalendarItemDisplayInfo();
	    	Expirable current = (Expirable)array[i];
	    	itemDisplayInfo.setExpirable((ExpirableRecordObject)array[i]);
	    	if (i > 0) {
	    		ExpirableRecordObject previous = (ExpirableRecordObject)array[i - 1];
	    		assert previous != null;
	    		assert current.getDateStart() != null;
				//find lowest lane that can be used, check last expirable in each lane.
				int lowestLane = 0;
				boolean laneOk = false;
				while (laneOk == false) {
					if (expirableCanOccupyLane(itemDisplayInfo, lowestLane, this)) {
						laneOk = true;
						itemDisplayInfo.setLaneNumber(lowestLane);
	    				if (maxLaneNumber < lowestLane) {
	    					maxLaneNumber = lowestLane;
	    				}
					} else {
						lowestLane++;
					}
				}
	    				
	    	} else {
	    		itemDisplayInfo.setLaneNumber(0);
	    	}
	    	this.setNumberOfRequiredLanes(maxLaneNumber + 1);
	    	this.add(itemDisplayInfo);
	    }
	}

	
	public int getNumberOfRequiredLanes() {
		return numberOfRequiredLanes;
	}

	public void setNumberOfRequiredLanes(int numberOfRequiredLanes) {
		this.numberOfRequiredLanes = numberOfRequiredLanes;
	}
	
	public CalendarItemDisplayInfo findItemDisplayInfo(ExpirableRecordObject expirable) {
		CalendarItemDisplayInfo found = null;
		Iterator iter = this.iterator();
		while ((iter.hasNext()) && (found == null)) {
			CalendarItemDisplayInfo currentInfo = (CalendarItemDisplayInfo)iter.next();
			ExpirableRecordObject currentExpirable = currentInfo.getExpirable();
			if (currentExpirable.getId().equals(expirable.getId())) {
				found = currentInfo;
			}
		}
		return(found);
	}
	
	
	private boolean expirableCanOccupyLane(CalendarItemDisplayInfo itemDisplayInfo, int laneNumber, CalendarItemsDisplayInfo listInfo) {
		boolean result = true;
		Date startDate = itemDisplayInfo.getExpirable().getDateStart();
		Iterator iter = listInfo.iterator();
		while ((iter.hasNext()) && (result == true)) {
			CalendarItemDisplayInfo current = (CalendarItemDisplayInfo)iter.next();
			if (current.getLaneNumber() == laneNumber) {
				if (current.getExpirable().getDateEnd().after(startDate)) {
					result = false;
				}
			}
		}
		return result;
	}

	public boolean canItemSpanAcrossAllLanes(CalendarItemDisplayInfo itemDisplayInfo) {
		boolean result = true;
		
		Iterator iter = this.iterator();
		while ((result == true) && (iter.hasNext())) {
			CalendarItemDisplayInfo current = (CalendarItemDisplayInfo)iter.next();
			log.debug("If current item is not item being checked, see if current item's dates overlap checked item");
			if (current.getExpirable().getId().equals(itemDisplayInfo.getExpirable().getId()) == false) {
				Set<Expirable> twoExpirables = new HashSet<Expirable>();
				twoExpirables.add(current.getExpirable());
				twoExpirables.add(itemDisplayInfo.getExpirable());
				ValidationProblemList problems = ExpirableUtils.validateNoOverlap(twoExpirables);
				if (problems.size() > 0) {
					result = false;
				}
			}
		}
		
		return(result);
	}

}
